home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-05-22 | 18.1 KB | 695 lines | [TEXT/MPCC] |
- //
- // File: TestFunctions.c
- //
- // Contains: Localized sound support for QuickTime VR movies.
- //
- // Written by: Tim Monroe
- //
- // Copyright: © 1996 by Apple Computer, Inc., all rights reserved.
- //
- // Change History (most recent first):
- //
- // <3> 01/24/97 rtm major code overhaul: got orientation working correctly;
- // moved most orientation processing to a prescreen routine;
- // implemented listener/sources/channels/resources on a per-instance basis
- // <2> 12/10/96 rtm added VR3DSound_GetSoundHeader and VR3DSound_GetSndBaseFrequency
- // <1> 12/09/96 rtm ported earlier 3D sound support functions to VRShell
- //
- //
- // ******************************************************************************
- // This file provides several functions to attach sounds to specific locations
- // in a panorama. As the viewer moves, the sounds should change their apparent position
- // accordingly. For example, if a sound is initially straight ahead of the viewer
- // and the viewer turns 90˚ right, the sound should then appear to come
- // from the viewer's left.
- //
- // Some of this code is straight out of the SoundSprocket documentation
- // (by yours truly!) or the SoundSprocket sample source (by Dan Venolia).
- //
- // The process of attaching a sound to a specific location might work like this:
- // (1) Create a hot spot of type 'snd ' in the panorama or object.
- // (2) Set the hot spot ID to be the 'snd ' resource ID of the sound for that location.
- // (3) Create a user data atom (or resource) that contains the distance of the sound source from the user.
- // This interface hasn't been implemented yet!!!
- // ******************************************************************************
- // TODO:
- // + this app reads the same sounds for all nodes and movies; fix that;
- // we need a mechanism for a sound to indicate which movie/node it belongs to;
- // + figure out position of sound source from locations of sound hot spots, as described above
-
-
- // header files
- #include "TestFunctions.h"
- #include "MacFramework.h"
- #include "QTVRUtilities.h"
-
-
- // system headers
- #include <Windows.h>
- #include <Resources.h>
- #include <TextUtils.h>
- #include <fp.h>
- #include <CodeFragments.h>
-
-
- // global variables
- extern Boolean gHasSoundSprockets; // is SoundSprockets available?
-
- // local prototypes
- static Boolean VR3DSound_CheckVersionNumber (const NumVersion *inVersion, UInt8 inMajor, UInt8 inMinor, UInt8 inBug);
- SoundHeaderPtr VR3DSound_GetSoundHeader (Handle theSndHandle);
- long VR3DSound_GetSndBaseFrequency (Handle theSndHandle);
-
-
- //////////
- //
- // VR3DSound_CheckVersionNumber
- // Returns true if the given version number is compatible with
- // (that is, not older than) version inMajor.inMinor.inBug.
- //
- //////////
-
- Boolean VR3DSound_CheckVersionNumber (
- const NumVersion* inVersion,
- UInt8 inMajor,
- UInt8 inMinor,
- UInt8 inBug)
- {
- if (inVersion->majorRev != inMajor) {
- return(inVersion->majorRev > inMajor);
- } else {
- return(inVersion->minorAndBugRev >= inMinor << 4 | inBug);
- }
- }
-
-
- //////////
- //
- // VR3DSound_GetSoundHeader
- // Returns a pointer to the sound header in a sampled sound resource.
- //
- //////////
-
- SoundHeaderPtr VR3DSound_GetSoundHeader (Handle theSndHandle)
- {
- SoundHeaderPtr mySndHeader = NULL;
- long myOffset = 0;
- OSErr myErr;
-
- myErr = GetSoundHeaderOffset((SndListHandle)theSndHandle, &myOffset);
- if (myErr == noErr) {
- mySndHeader = (SoundHeaderPtr)(*theSndHandle + myOffset);
- }
-
- return(mySndHeader);
- }
-
-
- //////////
- //
- // VR3DSound_GetSndBaseFrequency
- // Returns the base frequency of a sampled sound.
- // We need this when installing the sound as a voice, in VR3DSound_PlayResource.
- //
- //////////
-
- long VR3DSound_GetSndBaseFrequency (Handle theSndHandle)
- {
- SoundHeaderPtr mySndHeader;
- long myBaseFreq = 0;
-
- mySndHeader = VR3DSound_GetSoundHeader(theSndHandle);
- if (mySndHeader != NULL) {
- myBaseFreq = mySndHeader->baseFrequency;
- }
-
- return(myBaseFreq);
- }
-
-
- //////////
- //
- // VR3DSound_Init
- // Initialize for 3D localized sound.
- //
- //////////
-
- void VR3DSound_Init (void)
- {
- NumVersion myVersion;
-
- // check the Sound Manager version: we require version 3.2.1 or later
- myVersion = SndSoundManagerVersion();
- // ••• WARNING •••
- // • IF YOU CAN'T COMPILE THE PREVIOUS LINE, YOU MUST UPGRADE TO ETO #20 OR
- // • EDIT YOUR Sound.h TO MAKE SndSoundManagerVersion RETURN THE TYPE NumVersion.
-
- if (!VR3DSound_CheckVersionNumber(&myVersion, 3, 2, 1)) {
- ShowWarning("\pIncorrect version of the Sound Manager; we need 3.2.1 or later.", 0);
- ExitToShell();
- }
-
- // now make sure that SoundSprockets is available;
- // there is no Gestalt selector for this package, so we use an alternate strategy:
- if ((short)SSpListener_New == kUnresolvedSymbolAddress) {
- gHasSoundSprockets = false;
- ShowWarning("\pSoundSprockets is not installed; cannot do localized sound.", 0);
- } else {
- gHasSoundSprockets = true;
- }
-
- }
-
-
- //////////
- //
- // VR3DSound_InitWindowData
- // Initialize window-specific data for 3D sound.
- //
- //////////
-
- ApplicationDataHdl VR3DSound_InitWindowData (WindowObject theWindowObject)
- {
- #pragma unused(theWindowObject)
-
- OSStatus myErr;
- ApplicationDataHdl myAppData;
-
-
- myAppData = (ApplicationDataHdl)NewHandleClear(sizeof(ApplicationDataRecord));
- if (myAppData != NULL) {
-
- // lock the application data handle
- HLock((Handle)myAppData);
-
- // create a listener and set listener units to feet
- myErr = SSpListener_New(&(**myAppData).fListener);
- if (myErr == noErr) {
- SSpListener_SetMetersPerUnit((**myAppData).fListener, 0.3048);
- }
-
- // initialize other fields of application data structure
- (**myAppData).fMustUpdateOrient = true;
-
- // unlock the application data handle
- HUnlock((Handle)myAppData);
- }
-
- return(myAppData);
- }
-
-
- //////////
- //
- // VR3DSound_CreateLocalizedChannel
- // Create a localized sound channel.
- //
- //////////
-
- SndChannelPtr VR3DSound_CreateLocalizedChannel (void)
- {
- SndChannelPtr mySndChannel = nil;
- OSStatus myErr;
- SoundComponentLink myLink;
-
- // create a new sound channel
- myErr = SndNewChannel(&mySndChannel, sampledSynth, initMono, nil);
- if (myErr == noErr) {
-
- // install the 3D sound filters
- myLink.description.componentType = kSoundEffectsType;
- myLink.description.componentSubType = kSSpLocalizationSubType;
- myLink.description.componentManufacturer = 0;
- myLink.description.componentFlags = 0;
- myLink.description.componentFlagsMask = 0;
- myLink.mixerID = nil;
- myLink.linkID = nil;
-
- myErr = SndSetInfo(mySndChannel, siPreMixerSoundComponent, &myLink);
- if (myErr != noErr)
- mySndChannel = nil;
- }
-
- return(mySndChannel);
- }
-
-
- //////////
- //
- // VR3DSound_CreateLocalizedSource
- // Create a localized sound source.
- //
- //////////
-
- SSpSourceReference VR3DSound_CreateLocalizedSource (void)
- {
- SSpSourceReference mySource = nil;
- OSStatus myErr;
-
- // create a new sound source
- myErr = SSpSource_New(&mySource);
-
- return(mySource);
- }
-
-
- //////////
- //
- // VR3DSound_PlaySilence
- // Stop the sound playing from a particular source.
- //
- //////////
-
- void VR3DSound_PlaySilence (WindowObject theWindowObject, short theChannelIndex)
- {
- OSStatus myErr;
- SndCommand mySndCommand;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- if ((**myAppData).fChannels[theChannelIndex] == NULL)
- return;
-
- mySndCommand.cmd = quietCmd;
- mySndCommand.param1 = 0;
- mySndCommand.param2 = 0;
- myErr = SndDoImmediate((**myAppData).fChannels[theChannelIndex], &mySndCommand);
- }
-
-
- //////////
- //
- // VR3DSound_PlayResource
- // Play the snd resource of the given ID on a particular channel.
- // Currently, we just loop the sound indefinitely. We should provide
- // more options (via a parameter flag) for sound duration, repetition, etc.
- //
- //////////
-
- void VR3DSound_PlayResource (WindowObject theWindowObject, short theChannelIndex, short theResID)
- {
- OSStatus myErr;
- SndCommand mySndCommand;
- long myOffset;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- // lock the application data handle
- HLock((Handle)myAppData);
-
- // silence the sound channel
- VR3DSound_PlaySilence(theWindowObject, theChannelIndex);
-
- // get the resource
- (**myAppData).fResources[theChannelIndex] = (SndListHandle)GetResource('snd ', theResID);
- if ((**myAppData).fResources[theChannelIndex] == NULL || ResError() != noErr) {
- return;
- }
-
- // lock the resource data down
- HLockHi((Handle)(**myAppData).fResources[theChannelIndex]);
-
- // play the sound indefinitely
- GetSoundHeaderOffset((**myAppData).fResources[theChannelIndex], &myOffset);
-
- mySndCommand.cmd = soundCmd;
- mySndCommand.param1 = 0;
- mySndCommand.param2 = (long)*((**myAppData).fResources[theChannelIndex]) + myOffset;
- myErr = SndDoImmediate((**myAppData).fChannels[theChannelIndex], &mySndCommand);
-
- mySndCommand.cmd = freqCmd;
- mySndCommand.param1 = 0;
- mySndCommand.param2 = VR3DSound_GetSndBaseFrequency((Handle)(**myAppData).fResources[theChannelIndex]);
- myErr = SndDoImmediate((**myAppData).fChannels[theChannelIndex], &mySndCommand);
-
- // unlock the application data handle
- HUnlock((Handle)myAppData);
- }
-
-
- //////////
- //
- // VR3DSound_CountSoundHotSpotsInNode
- // Determine how many sound source hot spots are in the current node.
- //
- //////////NOT FULLY IMPLEMENTED!!!!
-
- UInt32 VR3DSound_CountSoundHotSpotsInNode (WindowObject theWindowObject)
- {
- OSStatus myErr = noErr;
- QTAtomContainer myNodeInfo;
- QTAtom myHotSpotParentAtom;
- short myHotSpotCount = 0;
- QTVRInstance myInstance;
-
- return(kMaxNumSourcesPerNode); //just cheating, for now!
-
- if (theWindowObject == NULL)
- return(0);
-
- myInstance = (**theWindowObject).fInstance;
-
- // get the node information atom container
- myErr = QTVRGetNodeInfo(myInstance, kQTVRCurrentNode, &myNodeInfo);
-
- // get the hot spot parent atom
- if (!myErr)
- myHotSpotParentAtom = QTFindChildByID(myNodeInfo, kParentAtomIsContainer, kQTVRHotSpotParentAtomType, 1, nil);
-
- // get the number of hot spot atoms in that parent
- if (myHotSpotParentAtom != 0) {
- myHotSpotCount = QTCountChildrenOfType(myNodeInfo, myHotSpotParentAtom, kQTVRHotSpotAtomType);
- }
-
- if (myHotSpotCount > kMaxNumSourcesPerNode)
- myHotSpotCount = kMaxNumSourcesPerNode;
-
- return((UInt32)myHotSpotCount);
- }
-
-
- //////////
- //
- // VR3DSound_Update3DSoundEnv
- // Update the virtual audio environment.
- //
- //////////
-
- void VR3DSound_Update3DSoundEnv (WindowObject theWindowObject)
- {
- short myIndex;
- UInt32 myCount;
- SSpLocalizationData myData;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- myCount = VR3DSound_CountSoundHotSpotsInNode(theWindowObject);
-
- // update the virtual audio environment
- for (myIndex = 0; myIndex < myCount; myIndex++) {
- SSpSource_CalcLocalization((**myAppData).fSources[myIndex], (**myAppData).fListener, &myData);
- SndSetInfo((**myAppData).fChannels[myIndex], siSSpLocalization, &myData);
- }
- }
-
-
- //////////
- //
- // VR3DSound_StartNodeSounds
- // Start playing all sounds for the current node.
- //
- //////////
-
- void VR3DSound_StartNodeSounds (WindowObject theWindowObject)
- {
- UInt32 myCount;
- short myIndex;
- //SSpLocationData theLoc;
- TQ3Point3D myPoint;
- TQ3Vector3D myOrient;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- // right now, just brute-force the locations
- myCount = VR3DSound_CountSoundHotSpotsInNode(theWindowObject);
-
- for (myIndex = 0; myIndex < myCount; myIndex++) {
- (**myAppData).fSources[myIndex] = VR3DSound_CreateLocalizedSource();
- (**myAppData).fChannels[myIndex] = VR3DSound_CreateLocalizedChannel();
- switch (myIndex) {
- case 0:
- myPoint.x = kObjectDistance;
- myPoint.y = 0.0;
- myPoint.z = 0.0;
- myOrient.x = -kObjectDistance;
- myOrient.y = 0.0;
- myOrient.z = 0.0;
- break;
- case 1:
- myPoint.x = 0.0;
- myPoint.y = 0.0;
- myPoint.z = kObjectDistance;
- myOrient.x = 0.0;
- myOrient.y = 0.0;
- myOrient.z = -kObjectDistance;
- break;
- case 2:
- myPoint.x = 0.0;
- myPoint.y = 0.0;
- myPoint.z = -kObjectDistance;
- myOrient.x = 0.0;
- myOrient.y = 0.0;
- myOrient.z = kObjectDistance;
- break;
- case 3:
- myPoint.x = -kObjectDistance;
- myPoint.y = 0.0;
- myPoint.z = 0.0;
- myOrient.x = kObjectDistance;
- myOrient.y = 0.0;
- myOrient.z = 0.0;
- break;
- }
-
- SSpSource_SetPosition((**myAppData).fSources[myIndex], &myPoint);
- SSpSource_SetOrientation((**myAppData).fSources[myIndex], &myOrient);
- SSpSource_SetAngularAttenuation((**myAppData).fSources[myIndex], kVRPi/8, 20.0);
-
- VR3DSound_Update3DSoundEnv(theWindowObject);
- VR3DSound_PlayResource(theWindowObject, myIndex, 128 + myIndex);
- }
- }
-
-
- //////////
- //
- // VR3DSound_StopNodeSounds
- // Stop playing all sounds for the specified node.
- //
- //////////
-
- void VR3DSound_StopNodeSounds (WindowObject theWindowObject)
- {
- short myIndex;
- UInt32 myCount;
-
- // get the number of sound source hot spots in this node
- myCount = VR3DSound_CountSoundHotSpotsInNode(theWindowObject);
-
- // stop playing any localized sounds associated with this node
- for (myIndex = 0; myIndex < myCount; myIndex++) {
- VR3DSound_PlaySilence(theWindowObject, myIndex);
- }
- }
-
-
- //////////
- //
- // VR3DSound_DumpNodeSounds
- // Release all sound resources for the specified node.
- //
- //////////
-
- void VR3DSound_DumpNodeSounds (WindowObject theWindowObject)
- {
- short myIndex;
- UInt32 myCount;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- // get the number of sound source hot spots in this node
- myCount = VR3DSound_CountSoundHotSpotsInNode(theWindowObject);
-
- // get rid of associated resources
- for (myIndex = 0; myIndex < myCount; myIndex++) {
- if ((**myAppData).fResources[myIndex] != NULL)
- ReleaseResource((Handle)(**myAppData).fResources[myIndex]);
- }
- }
-
-
- //////////
- //
- // VR3DSound_InstallPrescreenRoutine
- // Install a prescreen buffer imaging complete procedure
- // to alter the position of the listener based on the user's panning or tilting.
- //
- //////////
-
- void VR3DSound_InstallPrescreenRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
- {
- ImagingCompleteUPP myImagingProc;
-
- myImagingProc = NewImagingCompleteProc(VR3DSound_PrescreenRoutine);
- QTVRSetPrescreenImagingCompleteProc(theInstance, myImagingProc, (SInt32)theWindowObject, 0);
-
- // we want our prescreen imaging procedure to be called *every* time a QTVR image is drawn,
- // so we must turn off direct screen drawing for all drawing modes (both motion and static)
- QTVRSetImagingProperty(theInstance, kQTVRAllModes, kQTVRImagingDirectDraw, 0);
- }
-
-
- //////////
- //
- // VR3DSound_InstallInterceptRoutine
- // Install a QTVR intercept procedure to signal that the listener orientation
- // needs to be updated.
- //
- //////////
-
- void VR3DSound_InstallInterceptRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
- {
- QTVRInterceptUPP myInterceptProc;
-
- myInterceptProc = NewQTVRInterceptProc(VR3DSound_InterceptRoutine);
-
- // we'll just use the same intercept proc for each intercepted procedure
- QTVRInstallInterceptProc(theInstance, kQTVRSetPanAngleSelector, myInterceptProc, (SInt32)theWindowObject, 0);
- QTVRInstallInterceptProc(theInstance, kQTVRSetTiltAngleSelector, myInterceptProc, (SInt32)theWindowObject, 0);
- }
-
-
- ///////////
- //
- // VR3DSound_PrescreenRoutine
- // Alter the position of the listener based on the user's panning or tilting.
- //
- //////////
-
- pascal OSErr VR3DSound_PrescreenRoutine (QTVRInstance theInstance, WindowObject theWindowObject)
- {
- float myPan;
- float myTilt;
- TQ3Vector3D myOrientation;
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return(paramErr);
-
- if (!(**myAppData).fMustUpdateOrient)
- return(noErr);
-
- // get the current pan and tilt angles (in radians)
- myPan = QTVRGetPanAngle(theInstance);
- myTilt = QTVRGetTiltAngle(theInstance);
-
- // figure out the orientation
- myOrientation.x = -sin(myPan) * cos(myTilt);
- myOrientation.y = sin(myTilt);
- myOrientation.z = -cos(myPan) * cos(myTilt);
-
- // set the new orientation of the listener
- SSpListener_SetOrientation((**myAppData).fListener, &myOrientation);
-
- // update the virtual audio environment
- VR3DSound_Update3DSoundEnv(theWindowObject);
-
- // clear the update flag
- (**myAppData).fMustUpdateOrient = false;
-
- return(noErr);
- }
-
-
- //////////
- //
- // VR3DSound_InterceptRoutine
- // signal that the listener orientation needs to be updated.
- //
- //////////
-
- pascal void VR3DSound_InterceptRoutine (QTVRInstance theInstance, QTVRInterceptPtr theMsg, WindowObject theWindowObject, Boolean *cancel)
- {
- #pragma unused(theInstance)
-
- Boolean myCancelInterceptedProc = false; // true == do NOT call thru; false == call thru
- ApplicationDataHdl myAppData;
-
- myAppData = (ApplicationDataHdl)GetAppDataFromWindowObject(theWindowObject);
- if (myAppData == NULL)
- return;
-
- switch (theMsg->selector) {
- case kQTVRSetPanAngleSelector:
- case kQTVRSetTiltAngleSelector:
- (**myAppData).fMustUpdateOrient = true;
- break;
-
- default:
- break;
- }
-
- *cancel = myCancelInterceptedProc;
- }
-
-
- //////////
- //
- // VR3DSound_EnteringNodeProc
- // A node-entering procedure: start any sounds attached to the new node.
- //
- //////////
-
- pascal OSErr VR3DSound_EnteringNodeProc (QTVRInstance theInstance, long nodeID, WindowObject theWindowObject)
- {
- #pragma unused(theInstance, nodeID)
-
- if (theWindowObject == NULL)
- return(paramErr);
-
- VR3DSound_StartNodeSounds(theWindowObject);
-
- return(noErr);
- }
-
-
- //////////
- //
- // VR3DSound_LeavingNodeProc
- // A node-leaving procedure: stop any sounds attached to the current node.
- //
- //////////
-
- pascal OSErr VR3DSound_LeavingNodeProc (QTVRInstance theInstance, long fromNodeID, long toNodeID, Boolean *cancel, WindowObject theWindowObject)
- {
- #pragma unused(theInstance, fromNodeID, toNodeID, cancel)
-
- if (theWindowObject == NULL)
- return(paramErr);
-
- VR3DSound_StopNodeSounds(theWindowObject);
- VR3DSound_DumpNodeSounds(theWindowObject);
-
- return(noErr);
- }
-
-
- //////////
- //
- // MyTestFunc
- //
- //////////
-
- void MyTestFunc (QTVRInstance theInstance)
- {
- QTVRSetPanAngle(theInstance, 0.0);
- QTVRSetTiltAngle(theInstance, 0.0);
- QTVRUpdate(theInstance, kQTVRStatic);
- }
-